package com.inwavetech.os.util;

import android.util.Base64;
import android.util.Log;

import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.message.BasicHeader;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.UUID;

/**
 * Created by jgskin on 2/7/14.
 */
public class AuthenticatedRequest extends Request {

    private static final int UNAUTHORIZED = 401;
    private final SessionManager sessionManager;

    public AuthenticatedRequest(SessionManager sessionManager) {
        super();

        this.sessionManager = sessionManager;
    }

    @Override
    protected HttpRequestBase configureMethod(HttpRequestBase method) {
        // get user token
        String token = sessionManager.getToken();

        // generate unique and random string
        String nonce = UUID.randomUUID().toString();

        // generate the request time one second b4 to accomodate slightly misconfigured devices' date and time
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.SECOND, -2);
        String created = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").format(calendar.getTime());

        // build the digest
        String digest = buildDigest(nonce, created, token);

        // format the credentials part of the header
        String credentialsPart = String.format("UsernameToken Username=\"%s\", PasswordDigest=\"%s\", Nonce=\"%s\", Created=\"%s\"",
                sessionManager.getUsername(), base64Encode(digest), base64Encode(nonce), created);

        // add the authentication headers
        method.addHeader(new BasicHeader("Authorization", "WSSE profile=\"UsernameToken\""));
        method.addHeader(new BasicHeader("X-WSSE", credentialsPart));

        return method;
    }

    @Override
    protected SimpleResponse fetchResponse(HttpRequestBase method) {
        SimpleResponse simpleResponse = super.fetchResponse(method);

        if (simpleResponse != null && simpleResponse.code == UNAUTHORIZED) {
            Log.i(AuthenticatedRequest.class.getName(), "Server rejected the user token, logging out!");
        }

        return simpleResponse;
    }

    private String base64Encode(String value) {
        return Base64.encodeToString(value.getBytes(), Base64.NO_WRAP);
    }

    private String buildDigest(String nonce, String created, String token) {
        String concatenatedSecret = String.format("%s%s%s", nonce, created, token);
        String digest = null;

        try {
            MessageDigest cript = MessageDigest.getInstance("SHA-1");
            cript.reset();
            cript.update(concatenatedSecret.getBytes("utf8"));

            byte[] mdbytes = cript.digest();

            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < mdbytes.length; i++) {
                sb.append(Integer.toString((mdbytes[i] & 0xff) + 0x100, 16).substring(1));
            }

            digest = sb.toString();
        } catch (NoSuchAlgorithmException e) {
            fail();
        } catch (UnsupportedEncodingException e) {
            fail();
        }

        return digest;
    }

    private void fail() {
        Log.e(AuthenticatedRequest.class.getName(), "Failed to build authentication resource");

        throw new RuntimeException("Failed to build authentication resource");
    }
}
